package com.study.zkyxgs1.utils;

import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.study.zkyxgs1.exception.ZkyxgsException;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * MongoDB操作工具类
 * @author weixiaochun
 * @date 2022/6/14 21:09
 */
@Slf4j
@Component("mongoDB")
public class MongoUtils<T> implements DBUtils<T> {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 获取实体的id值
     * @param entity 实体
     * @return
     */
    private Object getIdValue(T entity) {
        try {
            Field field = entity.getClass().getDeclaredField("id");
            field.setAccessible(true);
            return field.get(entity) != null
                    ? field.get(entity)
                    : UUID.randomUUID().toString().replace("-", "");
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
            log.error("{} --- error get id", entity);
            throw new ZkyxgsException(ExceptionConstant.CLASS_EXCEPTION.getCode(), ExceptionConstant.CLASS_EXCEPTION.getMessage());
        }
    }

    /**
     * 保存单条数据
     * @param data 保存的数据
     */
    @Override
    public void save(T data){
        if(data == null){
            log.error("data is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        mongoTemplate.save(data);
    }

    /**
     * 批量保存
     * @param entityList 保存的数据集合
     */
    @Override
    public void saveBatch(Collection<T> entityList) {
        mongoTemplate.insertAll(entityList);
    }

    /**
     * 根据id删除数据
     * @param id id
     * @param clazz 指定的class
     */
    @Override
    public boolean removeById(Object id, Class<T> clazz) {
        if(ObjectUtils.isEmpty(id)){
            log.error("id is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = Criteria.where("id").is(id);
        Query query = Query.query(criteria);
        DeleteResult deleteResult = mongoTemplate.remove(query, clazz);
        return deleteResult.getDeletedCount() > 0;
    }

    /**
     * 根据map条件删除数据
     * @param map map条件
     * @param clazz 指定class
     */
    @Override
    public void removeByMap(Map<String, Object> map, Class<T> clazz) {
        if(map == null || map.size() == 0){
            log.error("map is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        map.forEach((k, v) -> criteria.and(k).is(v));
        Query query = Query.query(criteria);
        mongoTemplate.remove(query, clazz);
    }

    /**
     * 根据id更新数据
     * @param data 更新数据
     * @return
     */
    @Override
    public boolean updateById(T data) {
        if(data == null){
            log.error("data is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        criteria.and("id").is(getIdValue(data));
        Query query = Query.query(criteria);
        Update update = new Update();
        try{
            for (Field field : data.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(data) != null){
                    update.set(field.getName(), field.get(data));
                }
            }
        }catch (IllegalAccessException e){
            e.printStackTrace();
            log.error("update data error\ndata: {}", data);
            throw new ZkyxgsException(ExceptionConstant.CLASS_EXCEPTION.getCode(), ExceptionConstant.CLASS_EXCEPTION.getMessage());
        }
        UpdateResult result = mongoTemplate.updateFirst(query, update, data.getClass());
        return result.getModifiedCount() == 1;
    }

    /**
     * 批量根据id更新数据
     * @param list 更新数据集合
     */
    @Override
    public void updateBatchById(Collection<T> list) {
        list.forEach(this::updateById);
    }

    /**
     * 根据id查询数据
     * @param id id
     * @param clazz 指定的class
     * @return
     */
    @Override
    public T getById(Object id, Class<T> clazz) {
        if(ObjectUtils.isEmpty(id)){
            log.error("id is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        return mongoTemplate.findById(id, clazz);
    }

    /**
     * 根据查询条件，查询指定class的一条数据
     * @param query 查询条件
     * @param clazz 指定的class
     * @return
     */
    public T getOne(Query query, Class<T> clazz) {
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 查询所有
     * @param clazz 指定class
     * @return
     */
    @Override
    public List<T> getAll(Class<T> clazz) {
        return mongoTemplate.findAll(clazz);
    }

    /**
     * 根据单个条件查询数据
     * @param key 数据的key
     * @param val 数据的val
     * @param clazz 指定的class
     * @return
     */
    @Override
    public List<T> getByOneQuery(String key, Object val, Class<T> clazz){
        if(key == null){
            log.error("key is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        criteria.and(key).is(val);
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 根据实体的数据进行查询
     * @param data 实体类
     * @param clazz 指定的class
     * @return
     */
    @Override
    public List<T> getByEntity(T data, Class<T> clazz){
        if(data == null){
            log.error("data is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        try{
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(data) != null){
                    criteria.and(field.getName()).is(field.get(data));
                }
            }
        }catch (IllegalAccessException e){
            e.printStackTrace();
            log.error("query data error\ndata: {}", data);
            throw new ZkyxgsException(ExceptionConstant.CLASS_EXCEPTION.getCode(), ExceptionConstant.CLASS_EXCEPTION.getMessage());
        }
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 根据范围查询数据
     * @param key 条件的key
     * @param start 开始的值
     * @param end 结束的值
     * @param clazz 指定的class
     * @return
     */
    @Override
    public List<T> getByRange(String key, Object start, Object end, Class<T> clazz){
        if(StringUtils.isBlank(key)){
            log.error("key is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        criteria.and(key).gte(start).lte(end);
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 根据集合查询数据
     * @param key 条件的key
     * @param list 条件的值集合
     * @param clazz 指定的class
     * @return
     */
    @Override
    public List<T> getByList(String key, Collection list, Class<T> clazz){
        if(StringUtils.isBlank(key)){
            log.error("key is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        criteria.and(key).in(list);
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 根据嵌套参数查询数据
     * @param clazz 指定的class
     * @param val 条件的val
     * @param keys 嵌套条件的keys
     * @return
     */
    @Override
    public List<T> getByNest(List<String> keys, Object val, Class<T> clazz){
        if(keys == null || keys.size() == 0){
            log.error("keys is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        StringBuffer sb = new StringBuffer();
        for (String s : keys) {
            sb.append(".").append(s);
        }
        String key = sb.substring(1);

        Criteria criteria = new Criteria();
        criteria.and(key).is(val);
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 根据嵌套参数查询数据
     *
     * @param map   条件val
     * @param clazz 指定的class
     * @return
     */
    @Override
    public List<T> getByNest(Map<String, Object> map, Class<T> clazz) {
        if(map == null){
            log.error("map is null");
            throw new ZkyxgsException(ExceptionConstant.DATA_EXCEPTION.getCode(), ExceptionConstant.DATA_EXCEPTION.getMessage());
        }
        Criteria criteria = new Criteria();
        for (String key : map.keySet()) {
            StringBuffer sb = new StringBuffer();
            sb.append(".").append(key);
            joint(map, key, sb, criteria);
        }
        Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz);
    }

    /**
     *
     * @param map 当前map
     * @param key 当前map中的key
     * @param sb 嵌套key拼接字符串
     * @param criteria 多条件构造
     */
    public void joint(Map map, String key, StringBuffer sb, Criteria criteria){
        if(map.get(key) instanceof Map){
            Map m = (Map) map.get(key);
            for (Object k : m.keySet()) {
                sb.append(".").append(k);
                joint(m, k.toString(), sb, criteria);
                sb.delete(sb.indexOf(k.toString()) - 1, sb.length());
            }
        }else{
            criteria.and(sb.substring(1)).is(map.get(key));
        }

    }

}
